#ifndef __M_POLLER_H__
#define __M_POLLER_H__

#pragma once

#include "log.hpp"
#include "channel.hpp"
#include <unordered_map>
#include <vector>
#include <cstring>
#include <sys/epoll.h>
#include <unistd.h>
#include <errno.h>

#define MAX_EPOLL_EVENTS 1024

class Poller
{
private:
    int _epfd;
    struct epoll_event _events[MAX_EPOLL_EVENTS];//poller能监控的最大连接数
    std::unordered_map<int, Channel*> _channels;//key: fd

private:
    int Create() { return epoll_create(1); }

    int Control(int optype, struct epoll_event* event) { return epoll_ctl(_epfd, optype, event->data.fd, event); }

public:
    Poller()
    {
        _epfd = Create();
        if (_epfd == -1)
        {
            LOG(DEG, "create epoll falied");
            exit(-1);
        }
    }

    ~Poller()
    {
        if (_epfd != -1)
            close(_epfd);
        // for (auto& it : _channels)
        // {
        //     if (it.second)
        //         delete it.second;
        // }
    }

    Channel* findChannel(int fd)
    {
        auto it = _channels.find(fd);
        if (it == _channels.end())
            return nullptr;
        return it->second;
    }

    int controlEpollEvent(Channel* channel, int optype)
    {
        struct epoll_event ev;
        ev.data.fd = channel->getFd();
        ev.events = 0;
        if (optype == EPOLL_CTL_ADD)//添加事件监控
        {
            _channels.insert(std::make_pair(channel->getFd(), channel));//将channel添加到poller的hash中 以此标识当前连接在epoll模型中
            ev.events |= channel->getEvent();
            return Control(optype, &ev);
        }
        else if (optype == EPOLL_CTL_MOD)//修改事件监控
        {
            if (findChannel(channel->getFd()) == nullptr)//在修改或移除事件监控时必须保证channel在hash中
            {
                LOG(DEG, "fd:%d not in epoll", channel->getFd());
                return -1;
            }
            ev.events |= channel->getEvent();
            return Control(optype, &ev);
        }
        else if (optype == EPOLL_CTL_DEL)//移除事件监控
        {
            if (findChannel(channel->getFd()) == nullptr)
            {
                LOG(DEG, "fd:%d not in epoll", channel->getFd());
                return -1;
            }
            _channels.erase(channel->getFd());//将channel从poller的hash中移除 以此标识连接不在epoll模型中
            //TODO 要不要在这里把channel对应的连接描述符释放
            return Control(optype, &ev);
        }
        else
        {
            LOG(DEG, "unknown optype");
            return -1;
        }
    }

    void Wait(std::vector<Channel*>* actives)//开始事件监控 返回事件就绪的连接
    {
        int n = epoll_wait(_epfd, _events, MAX_EPOLL_EVENTS, -1);
        if (n == -1)
        {
            if (errno == EINTR)
            {
                LOG(DEG, "signal interrupt epoll wait");
                return;
            }
            LOG(DEG, "epoll wait error: %s", strerror(errno));
            exit(-1);
        }
        for (int i = 0; i < n; i++)
        {
            Channel* channel = findChannel(_events[i].data.fd);
            if (channel == nullptr)//事件就绪但channel却不在hash中
            {
                LOG(DEG, "fd:%d not in epoll", _events[i].data.fd);
                exit(-1);
            }
            channel->setRevents(_events[i].events);//设置就绪事件 以便channel根据就绪事件类型调用相应回调处理事件
            actives->push_back(channel);//保存已有事件就绪的连接
        }
    }

};

#endif
